const express = require('express')
const router = express.Router()
const result = require('../utils/response')
const opera = require('../utils/dbOperation')
const db = require('../utils/db')
const ti = require('../utils/timeUtils')
const mysql = require('mysql')
const execSync = require('child_process').execSync
const path = require('path')
const fs = require('fs')
const uid = require('../utils/getUid')

router.get('/list', (req, res) => {
  opera.select('pro_project', { 'del_flag': 0 }, (results) => {
    let flag;
    if (results){
      flag = result.success(results);
    }else {
      flag = result.fail('数据库连接失败,请进入系统配置重新配置数据库信息');
    }
    res.send(flag)
  },null,'create_time')
})

/* 判断文件存在 */
const isFileExisted = (path, jarPath) => {
  return new Promise((resolve, reject) => {
    fs.access(path, (err) => {
      if (err) {
        // 文件不存在则创建
        fs.mkdirSync(path)
      }
      fs.copyFileSync(`${jarPath}/gen_project_main.jar`, `${path}/gen_project_main.jar`)
      fs.copyFileSync(`${jarPath}/application.properties`, `${path}/application.properties`)
      resolve()
    })
  })
}

router.post('/genProject', async (req, res) => {
  const id = req.body.id
  const localPath = req.body.localPath
  const proType = req.body.proType
  const jarPath = path.join(__dirname, '../lib')
  const srcPath = __dirname.split('AutoCode')[0]

  const strPathName = path.join(srcPath, './AutoCode')

  // 监测文件夹是否存在，不存在则创建 ，并copy jar包
  await isFileExisted(strPathName, jarPath)

  // 调用jar 包生成文件
  const flag = execSync(`cd ${strPathName}/; java -jar gen_project_main.jar ${id} ${proType} ${localPath} --spring.config.location=application.properties `)

  res.send(result.success())
})


router.get('/detail', (req, res) => {
  opera.findOne('pro_project', { 'id': req.query.id }, (results) => {
    opera.select('pro_project_database_rel', { 'project_id': results.id }, (resultss) => {
      const arrId = []
      for (let item of resultss) {
        arrId.push(item.database_id)
      }
      results['dbIds'] = arrId
      res.send(result.success(results))
    })
  })
})

router.put('/update', (req, res) => {
  const data = req.body
  const id = data.id
  const dbIds = data.dbIds
  delete data.dbIds
  delete data.id

  opera.update('pro_project', data, { 'id': id }, (results) => {
    if (dbIds) {
      opera.del('pro_project_database_rel', { 'project_id': id }, (resultss) => {
        const time = ti.getNowFormatDate()
        for (let dbId of dbIds) {
          opera.insert('pro_project_database_rel', {
            'id': uid(),
            'project_id': id,
            'database_id': dbId,
            'create_time': time,
            'update_time': time
          }, (resultsss) => {
          })
        }
        res.send(result.success())
      })
    } else {
      res.send(result.success())
    }
  })
})

router.delete('/delete', (req, res) => {
  const data = req.body
  opera.update('pro_project', { 'del_flag': 1 }, { 'id': data.id }, (results) => {
    res.send(result.success())
  })
})

router.post('/insert', (req, res) => {
  const data = req.body
  const time = ti.getNowFormatDate()
  data.id = uid()
  data.create_time = time
  data.update_time = time
  const dbIds = data.dbIds
  delete data.dbIds
  opera.insert('pro_project', data, (results) => {
    for (let dbId of dbIds) {
      opera.insert('pro_project_database_rel', {
        'id': uid(),
        'project_id': data.id,
        'database_id': dbId,
        'create_time': time,
        'update_time': time
      }, (resultsss) => {
      })
    }

    res.send(result.success())
  })
})
//let arr = [];

router.get('/treeStructure', async (req, res) => {
  const projectId = req.query.projectId
  const arr = []
  await opera.findOne('pro_project', { 'id': projectId }, (results) => {
    // 判断类型 是微服务还是单应用
    const type = results.pro_type
    // 设置一个唯一id 用于前端tree标识上下级
    const treeId = { 'index': 0 }

    const callback = data => {
      res.send(result.success(data))
    }

    if (type === 'micro') {
      getMicro(arr, treeId, projectId, results, callback)
    } else {
      getSimp(arr, treeId, results, callback)
    }
  })
})

const getMicro = (arr, treeId, projectId, mProject, callback) => {

  // 查询所有微服务
  db.query('select * from pro_micro_service where del_flag = 0 and project_id = ? order by name asc', [projectId], (results) => {
    let index = 1
    // 控制回调函数，循环完毕之后再回调~
    const back = (back) => {
      if (index === results.length) {
        callback(back)
      }
      index++
    }

    for (let item of results) {
      // 一层层往下查找
      getProjectTreeBy(arr, treeId, mProject, item, back)
    }
  })
}

const getSimp = (arr, treeId, mProject, callback) => {
  getProjectTreeBy(arr, treeId, mProject, null, callback)
}

const getProjectTreeBy = (arr, treeId, mProject, microService, callback) => {
  const projectId = mProject.id
  let commProModule = {}
  let microId = '0'

  // 设置条件
  const where = { 'project_id': projectId, 'del_flag': 0 }

  if (microService) {
    microId = microService.id
    commProModule.micro_id = microId
    where.micro_id = microService.id
  }

  let modules = []

  // 查询每个微服务下的模块
  opera.select('pro_module', where, (results) => {
    modules = results
    modules.push(commProModule)
    let srcName = 'src'

    if (microService) {
      srcName = microService.artifact_id + '(' + microService.name + ')'
    }

    // 统一格式处理 {id,indentify,pId,fileId,name,fileType,microId}
    const src = newJson(treeId, null, '', srcName, 'folder', '', microId)
    // 设置打开，前端tree显示用
    src.open = true

    arr.push(src)
    // 下层寻找
    processProjectByHierarchical(arr, treeId, src, mProject, modules, microService, callback)

  })
}

const processProjectByHierarchical = (arr, treeId, src, mProject, modules, microService, callback) => {
  const projectId = mProject.id
  const groupId = mProject.group_id
  const artifctId = mProject.artifact_id
  let microId = '0'
  let basePkgName = groupId + '.' + artifctId

  if (null != microService) {
    microId = microService.id
    basePkgName = groupId + '.' + mProject.artifact_id.replace(new RegExp('-', 'gm'), '_')
  }

  let pid = src.id
  // 统一格式处理 {id,indentify,pId,fileId,name,fileType,microId}
  let artifctIdNode = newJson(treeId, pid, '', basePkgName, 'package', '', microId)
  artifctIdNode.open = true
  pid = artifctIdNode.id
  arr.push(artifctIdNode)
  // 查找每个模块下的文件
  let index = 1
  for (let module of modules) {
    // and条件
    const andWhere = { 'del_flag': 0 }
    // or 条件
    const orWhere = {}
    let moduleMicroId = module.micro_id

    if (module.id && module.id !== '0') {
      andWhere.project_id = projectId
      andWhere.module_id = module.id
      if (moduleMicroId) {
        // 单项目为空
        andWhere.micro_id = moduleMicroId
      }
    } else {
      andWhere.project_id = null
      //orWhere.module_id = "0";
      orWhere.project_id = projectId
      orWhere.del_flag = 0
      if (moduleMicroId) {
        // 单项目为空
        orWhere.micro_id = moduleMicroId
      }
    }

    // 查询文件
    opera.select('pro_file', andWhere, (results) => {
      let modulePid = pid
      let controllerNode
      let serviceNode
      let mapperNode
      let voNode
      let domainNode

      if ('standard' === mProject.package_type) {
        controllerNode = newJson(treeId, modulePid, '', 'controller', 'package', '', microId)
        serviceNode = newJson(treeId, modulePid, '', 'service', 'package', '', microId)
        mapperNode = newJson(treeId, modulePid, '', 'mapper', 'package', '', microId)
        voNode = newJson(treeId, modulePid, '', 'vo', 'package', '', microId)
        domainNode = newJson(treeId, modulePid, '', 'domain', 'package', '', microId)

      } else {
        let _module = module.name
        if (_module) {
          let moduleNode = newJson(treeId, pid, '', _module, 'package', '', microId)
          modulePid = moduleNode.id
          arr.push(moduleNode)
        } else {
          _module = ''
        }
        controllerNode = newJson(treeId, modulePid, _module, 'controller', 'package', '', microId)
        serviceNode = newJson(treeId, modulePid, _module, 'service', 'package', '', microId)
        mapperNode = newJson(treeId, modulePid, _module, 'mapper', 'package', '', microId)
        voNode = newJson(treeId, modulePid, _module, 'vo', 'package', '', microId)
        domainNode = newJson(treeId, modulePid, _module, 'domain', 'package', '', microId)
      }

      arr.push(controllerNode)
      arr.push(serviceNode)
      arr.push(mapperNode)
      arr.push(voNode)
      arr.push(domainNode)

      let controllerNodeId = controllerNode.id
      let serviceNodeId = serviceNode.id
      let mapperNodeId = mapperNode.id
      let voNodeId = voNode.id
      let domainNodeId = domainNode.id

      for (let file of results) {
        const fileType = file.file_type
        const classType = file.class_type
        let _pid = null

        switch (fileType) {
          case 'controller':
            _pid = controllerNodeId
            break
          case 'service':
            _pid = serviceNodeId
            break
          case 'mapper':
            _pid = mapperNodeId
            break
          case 'vo':
            _pid = voNodeId
            break
          case 'domain':
            _pid = domainNodeId
            break
        }
        const node = newJson(treeId, _pid, '', file.name + '.java', fileType, file.id, microId)
        node.classType = classType
        arr.push(node)
      }

      if (index === modules.length) {
        callback(arr)
      }
      index++
    }, orWhere)
  }
}

const newJson = (treeId, pId, moduleName, name, fileType, fileId, microId) => {
  treeId.index++
  return {
    'id': treeId.index,
    'indentify': moduleName + '_' + name,
    'pId': pId,
    'fileId': fileId,
    'name': name,
    'fileType': fileType,
    'microId': microId
  }
}

router.post('/asyncDatabase', async (req, res) => {
  const data = req.body

  const findData = () => {
    return new Promise(((resolve, reject) => {
      opera.findOne('pro_database', data, (callBack) => resolve(callBack))
    }))
  }

  const databaseRes = await findData()

  const dbConfig = {
    host: databaseRes.hostname,
    user: databaseRes.username,
    password: databaseRes.password,
    database: databaseRes.db_name
  }
  const showTables = () => {
    return new Promise((resolve, reject) => {
      concatDb('show table status', [], dbConfig, (dbBack) => resolve(dbBack))
    })
  }
  const dbRes = await showTables()
  const timeData = ti.getNowFormatDate()

  const tablePro = (andWhere) => {
    return new Promise((resolve, reject) => {
      opera.select('pro_table', andWhere, (tableBack) => resolve(tableBack))
    })
  }

  const updateTable = (id, tableData) => {
    return new Promise((resolve, reject) => {
      opera.update('pro_table', tableData, { id: id }, (back) => resolve(id))
    })
  }

  const insertTable = (tableData) => {
    return new Promise((resolve, reject) => {
      opera.insert('pro_table', tableData, (back) => resolve(tableData.id))
    })
  }

  const columnsPro = (tableName, dbCif) => {
    return new Promise((resolve, reject) => {
      concatDb(`show full columns from ${tableName}`, [], dbCif, (columnsBack) => resolve(columnsBack))
    })
  }

  const tableColumnsPro = (columnsWhere) => {
    return new Promise((resolve, reject) => {
      opera.select('pro_table_column', columnsWhere, (back) => resolve(back))
    })
  }

  for (const item of dbRes) {
    const tableName = item.Name
    const comment = item.Comment

    let tableId

    // 判断表在数据库是否存在
    const andWhere = { name: tableName, comment: comment }
    const tableBack = await tablePro(andWhere)

    if (tableBack.length > 0) {
      const tableData = tableBack[0]
      tableData.comment = comment
      tableData.create_time = timeData
      tableData.update_time = timeData
      const id = tableData.id
      delete tableData.id
      tableId = await updateTable(id, tableData)
    } else {
      const tableData = {
        id: uid(),
        name: tableName,
        comment: comment,
        database_id: data.id,
        update_time: timeData,
        create_time: timeData
      }
      tableId = await insertTable(tableData)
    }
    const columnsRes = await columnsPro(tableName, dbConfig)
    const nameArr = []

    for (const columnsItem of columnsRes) {

      const columnsData = {
        key_type: columnsItem.Key,
        extra: columnsItem.Extra,
        comment: columnsItem.Comment,
        default_value: columnsItem.Default || null,
        name: columnsItem.Field,
        mysql_type: columnsItem.Type,
        allow_null: columnsItem.Null,
        table_id: tableId,
        update_time: timeData
      }
      nameArr.push(columnsItem.Field.toString())
      const columnsWhere = { table_id: tableId, name: columnsItem.Field }
      const tableColumnRes = await tableColumnsPro(columnsWhere)

      if (tableColumnRes.length > 0) {
        opera.update('pro_table_column', columnsData, { id: tableColumnRes[0].id })
      } else {
        columnsData.create_time = timeData
        columnsData.id = uid()
        opera.insert('pro_table_column', columnsData)
      }
    }

    if (nameArr.length > 0) {
      db.query('delete from pro_table_column where name not in (?) and table_id = ?', [nameArr, tableId])
    }
  }

  res.send(result.success())
})

const concatDb = (sql, params, dbConfig, callback) => {

  const connection = mysql.createConnection(dbConfig)
  connection.connect(function(err) {
    if (err) {
      console.log('数据库链接失败')
      throw err
    }

    connection.query(sql, params, function(err, results, fields) {
      if (err) {
        console.log('数据操作失败')
        throw err
      }
      callback && callback(JSON.parse(JSON.stringify(results)))
      connection.end(function(err) {
        if (err) {
          console.log('关闭数据库连接失败！')
          throw err
        }
      })
    })
  })
}


module.exports = router
